#include "kf_memory_block.h"

namespace kf
{
   namespace
   {
      byte bytetohex(byte b)
      {
         if((b&0x0f)<10)
         {
            return '0' + (b&0x0f);
         }
         else
         {
            return 'A' + ((b&0x0f)-10);
         }
      }
   }

   MemBlock::MemBlock()
   {
      m_begin = NULL;
      m_end = NULL;
      m_current = m_begin;
   }

   MemBlock::MemBlock(byte *start, dword length)
   {
      m_begin = start;
      m_end = m_begin + length;
      m_current = m_begin;
   }

   MemBlock::~MemBlock()
   {
   }

   void MemBlock::set_range(byte *start, dword length)
   {
      m_begin = start;
      m_end = m_begin + length;
      m_current = m_begin;
   }

   void MemBlock::seek(dword offset, bool relative)
   {
      if(relative)
      {
         m_current += offset;
      }
      else
      {
         m_current = m_begin + offset;
      }

      if(m_current > m_end)
      {
         m_current = m_end;
      }
   }

   MemBlock &MemBlock::operator+=(dword offset)
   {
      m_current += offset;
      if(m_current > m_end)
      {
         m_current = m_end;
      }
      return *this;
   }

   MemBlock &MemBlock::operator-=(dword offset)
   {
      m_current -= offset;
      if(m_current < m_begin)
      {
         m_current = m_begin;
      }
      return *this;
   }

   MemBlock &MemBlock::operator++()
   {
      m_current++;
      if(m_current > m_end)
      {
         m_current = m_end;
      }
      return *this;
   }

   MemBlock &MemBlock::operator--()
   {
      m_current--;
      if(m_current < m_begin)
      {
         m_current = m_begin;
      }
      return *this;
   }

   bool MemBlock::operator==(const MemBlock &rhs)
   {
      return m_current == rhs.m_current;
   }

   MemBlock &MemBlock::operator=(const MemBlock &rhs)
   {
      m_current = rhs.m_current;
      m_begin = rhs.m_begin;
      m_end = rhs.m_end;
      return *this;
   }

   byte *MemBlock::begin() const
   {
      return m_begin;
   }

   byte *MemBlock::end() const
   {
      return m_end;
   }

   byte *MemBlock::current() const
   {
      return m_current;
   }

   dword MemBlock::size() const
   {
      return (dword)(m_end - m_begin);
   }

   dword  MemBlock::get_dword()
   {
      byte *temp = m_current;
      operator+=(sizeof(dword));
      return *((dword *)temp);
   }

   sdword MemBlock::get_sdword()
   {
      byte *temp = m_current;
      operator+=(sizeof(sdword));
      return *((sdword *)temp);
   }

   word   MemBlock::get_word()
   {
      byte *temp = m_current;
      operator+=(sizeof(word));
      return *((word *)temp);
   }

   sword  MemBlock::get_sword()
   {
      byte *temp = m_current;
      operator+=(sizeof(sword));
      return *((sword *)temp);
   }

   byte   MemBlock::get_byte()
   {
      byte *temp = m_current;
      operator+=(sizeof(byte));
      return *((byte *)temp);
   }

   sbyte  MemBlock::get_sbyte()
   {
      byte *temp = m_current;
      operator+=(sizeof(sbyte));
      return *((sbyte *)temp);
   }

   float  MemBlock::get_float()
   {
      byte *temp = m_current;
      operator+=(sizeof(float));
      return *((float *)temp);
   }

   double MemBlock::get_double()
   {
      byte *temp = m_current;
      operator+=(sizeof(double));
      return *((double *)temp);
   }

   void MemBlock::get_block(byte *destination, dword length)
   {
      byte *temp = m_current;
      operator+=(length);
      memcpy(destination,temp,(dword)(m_current-temp));
   }

   std::string MemBlock::get_string(dword length)
   {
      std::string s;
      byte *temp = m_current;
      if(length>m_end-m_begin)
      {
         length = m_end-m_begin;
      }
      while(m_current<m_end)
      {
         if(*m_current == 0 || (length>0 && (dword)(m_current-temp)>=length))
         {
            s = std::string(temp, m_current);
            if(length>0)
            {
               m_current = temp + length;
            }
            else
            {
               m_current++;
            }
            return s;
         }
         m_current++;
      }
      return std::string(temp,m_end);
   }


   void MemBlock::set_dword(dword value)
   {
      *((dword *)m_current) = value;
      operator+=(sizeof(dword));
   }

   void MemBlock::set_sdword(sdword value)
   {
      *((sdword *)m_current) = value;
      operator+=(sizeof(sdword));
   }

   void MemBlock::set_word(word value)
   {
      *((word *)m_current) = value;
      operator+=(sizeof(word));
   }

   void MemBlock::set_sword(sword value)
   {
      *((sword *)m_current) = value;
      operator+=(sizeof(sword));
   }

   void MemBlock::set_byte(byte value)
   {
      *((byte *)m_current) = value;
      operator+=(sizeof(byte));
   }

   void MemBlock::set_sbyte(sbyte value)
   {
      *((sbyte *)m_current) = value;
      operator+=(sizeof(sbyte));
   }

   void MemBlock::set_float(float value)
   {
      *((float *)m_current) = value;
      operator+=(sizeof(float));
   }

   void MemBlock::set_double(double value)
   {
      *((double *)m_current) = value;
      operator+=(sizeof(double));
   }

   void MemBlock::set_block(byte *src, dword length)
   {
      byte *temp = m_current;
      operator+=(length);
      memcpy(temp,src,m_current-temp);
   }

   void MemBlock::set_string(const std::string &value, bool include_terminator, dword length, bool pad)
   {
      byte *temp = m_current;
      dword stringlength = dword(value.size()) + (include_terminator?1:0);
      
      
      if(length > 0)
      {
         if(length < stringlength)
         {
            stringlength = length - (include_terminator?1:0);
         }
         operator+=(length);
      }
      else
      {
         operator+=(stringlength);
      }
      memcpy(temp,value.c_str(),stringlength);
      if(length > 0 && stringlength < length && pad)
      {
         temp += stringlength;
         for(dword i=0;i<length-stringlength;i++)
         {
            if(temp >= m_end)
            {
               return;
            }
            *(temp++) = 0;
         }
      }
   }


   std::ostream &operator<<(std::ostream &os, const MemBlock &src)
   {
      byte c;
      byte buf[81]={0};
      dword i,j;
      dword address;
      MemBlock mi(src);
      mi.seek(0);

      for(j = 0; j < (mi.size()+15) / 16; j++)
      {
         for(i = 0; i < 79; i++)
         {
            buf[i]=' ';
         }
         address = ((dword)mi.begin())+j*16;
         for(i=0;i<8;i++)
         {
            buf[i]=bytetohex(address>>((7-i)*4));
         }

         for(i = 0; i < 16; i++)
         {
            if(i + j * 16 >= mi.size())
            {
               break;
            }
            c = mi.get_byte();
            buf[i * 3 + 9 + (i/4)] = bytetohex(c >> 4);
            buf[i * 3 + 10 + (i/4)] = bytetohex(c & 0x0f);

            if(c>=32 && c<=127)
            {
               buf[63 + i] = c;
            }
            else
            {
               buf[63 + i] = '.';
            }
         }
         os<<buf<<"\n";
      }
      return os;
   }
}
